package edu.iscas.xcheck.analysis.dom.matcher;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;

import edu.iscas.xcheck.analysis.dom.model.DomNode;
import edu.iscas.xcheck.analysis.dom.utils.DomUtils;

public class Matcher {

	float THRESHOLD_LEVEL = 0.75f;

	private Map<String, String> matched;
	private List<DomNode> unmatched1, unmatched2;
	
	private boolean isTest = false;

	public Matcher(Map<String, String> matched) {
		this.matched = matched;
		unmatched1 = new ArrayList<>();
		unmatched2 = new ArrayList<>();
	}

	public MatchResult doMatch(List<DomNode> addList1,
			Map<String, DomNode> addMap2, Map<String, DomNode> idMap2) {
		List<DomNode> workList = new ArrayList<DomNode>();
		// List<DomNode> workList2 = new ArrayList<DomNode>();
		Set<String> workSet2 = new HashSet<>();
		List<DomNode> textList = new ArrayList<DomNode>();
		List<DomNode> textList2 = new ArrayList<DomNode>();

		workList.addAll(addList1);
		workSet2.addAll(addMap2.keySet());
		// workList2.addAll(addMap2.values());
		// 1: Level match with perfect match
		Iterator<DomNode> it = workList.iterator();
		while (it.hasNext()) {
			DomNode node = it.next();
			node.setMatched(false);
			DomNode parent = node.getParent();
			if (parent != null) {
				String parentId_m = matched.get(parent.getNodeid());
				if (parentId_m == null)
					continue;
				DomNode paretn_m = idMap2.get(parentId_m);
				float maxIndex = 0;
				DomNode bestMatch = null;
				List<DomNode> childList = paretn_m.getChildren();
				if (childList == null)
					continue;
				for (DomNode cmpNode : childList) {
					if (!cmpNode.isMatched()) {
						if ((node.isText() && cmpNode.isText())
								|| (node.isTag() && cmpNode.isTag() && StringUtils
										.equals(node.getTagName(),
												cmpNode.getTagName()))) {
							float matchIndex = DomUtils.calculateMatchIndex(
									node, cmpNode);
							if (matchIndex == 1.0) {
								bestMatch = cmpNode;
								break;
							}
							if (matchIndex > maxIndex) {
								maxIndex = matchIndex;
								bestMatch = cmpNode;
							}
						}
					}
				}
				if (bestMatch != null) {
					matched.put(node.getHash(), bestMatch.getHash());
					bestMatch.setMatched(true);
					node.setMatched(true);
					it.remove();
					workSet2.remove(bestMatch.getHash());// 待测
				}
			}
		}
		// 2: Approximate global matching
		it = workList.iterator();
		while (it.hasNext()) {
			DomNode node = it.next();
			if (node.isText()) {
				textList.add(node);
				it.remove();
				continue;
			}
			float maxIndex = 0;
			DomNode bestMatch = null;
			Iterator<String> it2 = workSet2.iterator();
			while (it2.hasNext()) {
				String cmpKey = it2.next();
				DomNode cmpNode = addMap2.get(cmpKey);
				if (cmpNode.isText()) {
					textList2.add(cmpNode);
					it2.remove();
					continue;
				}
				if (!cmpNode.isMatched()) {
					if (cmpNode.isTag()
							&& StringUtils.equals(node.getTagName(),
									cmpNode.getTagName())) {
						float matchIndex = DomUtils.calculateMatchIndex(node,
								cmpNode);
						if (matchIndex == 1.0) {
							bestMatch = cmpNode;
							break;
						}
						if (matchIndex > maxIndex) {
							maxIndex = matchIndex;
							bestMatch = cmpNode;
						}
					}
				}
			}
			if (bestMatch != null) {
				matched.put(node.getHash(), bestMatch.getHash());
				bestMatch.setMatched(true);
				node.setMatched(true);
				it.remove();
				workSet2.remove(bestMatch.getHash());// 待测
			}
		}
		
		if(!isTest){
			// 3: Text Node Match
			Iterator<DomNode> textIt1 = textList.iterator();
			while (textIt1.hasNext()) {
				DomNode node = textIt1.next();
				Iterator<DomNode> textIt2 = textList2.iterator();
				while (textIt2.hasNext()) {
					DomNode node2 = textIt2.next();
					if (StringUtils.equals(matched.get(node.getPid()),
							node2.getPid())) {
						matched.put(node.getHash(), node2.getHash());
						node.setMatched(true);
						node2.setMatched(true);
						textIt2.remove();
						break;
					}
				}
			}
		}
		unmatched1.addAll(workList);
		for (String key : workSet2) {
			unmatched2.add(addMap2.get(key));
		}
		return new MatchResult(unmatched1, unmatched2);
	}
}
